<template>
  <div class="crud-image" :style="containerStyle">
    <el-image v-if="src" :src="src" :alt="alt" :style="imageStyle"
      :preview-src-list="preview && previewSrcList ? previewSrcList : preview ? [src] : undefined"
      :preview-teleported="true" fit="cover" @error="handleError">
      <template #error>
        <div class="crud-image__error">
          <el-icon v-if="errorIcon" :size="iconSize">
            <component :is="errorIcon" />
          </el-icon>
          <el-icon v-else :size="iconSize">
            <Picture />
          </el-icon>
          <span v-if="showErrorText" class="error-text">加载失败</span>
        </div>
      </template>
    </el-image>

    <div v-else class="crud-image__placeholder">
      <el-icon v-if="placeholder" :size="iconSize">
        <component :is="placeholder" />
      </el-icon>
      <el-icon v-else :size="iconSize">
        <Picture />
      </el-icon>
      <span v-if="showPlaceholderText" class="placeholder-text">暂无图片</span>
    </div>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { Picture } from '@element-plus/icons-vue'
import type { Component } from 'vue'

/**
 * 组件属性接口
 */
interface Props {
  /** 图片地址 */
  src?: string
  /** 图片描述 */
  alt?: string
  /** 图片宽度 */
  width?: number
  /** 图片高度 */
  height?: number
  /** 是否圆形 */
  round?: boolean
  /** 占位图标 */
  placeholder?: string | Component
  /** 错误时显示的图标 */
  errorIcon?: string | Component
  /** 是否显示预览 */
  preview?: boolean
  /** 预览图片列表 */
  previewSrcList?: string[]
  /** 是否显示错误文本 */
  showErrorText?: boolean
  /** 是否显示占位文本 */
  showPlaceholderText?: boolean
}

/**
 * 组件事件接口
 */
interface Emits {
  /** 图片加载错误事件 */
  (e: 'error', event: Event): void
}

/**
 * Props 定义
 */
const props = withDefaults(defineProps<Props>(), {
  width: 40,
  height: 40,
  round: false,
  preview: false,
  showErrorText: false,
  showPlaceholderText: false
})

/**
 * 事件定义
 */
const emit = defineEmits<Emits>()

/**
 * 容器样式
 */
const containerStyle = computed(() => ({
  width: `${props.width}px`,
  height: `${props.height}px`,
  borderRadius: props.round ? '50%' : '6px',
  overflow: 'hidden',
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  backgroundColor: '#f5f7fa',
  border: '1px solid #e4e7ed'
}))

/**
 * 图片样式
 */
const imageStyle = computed(() => ({
  width: '100%',
  height: '100%',
  borderRadius: props.round ? '50%' : '6px'
}))

/**
 * 图标大小
 */
const iconSize = computed(() => {
  const size = Math.min(props.width, props.height)
  return Math.max(12, Math.min(size * 0.4, 24))
})

/**
 * 处理图片加载错误
 * @param event - 错误事件
 */
const handleError = (event: Event): void => {
  emit('error', event)
}
</script>

<style lang="scss" scoped>
.crud-image {
  position: relative;

  :deep(.el-image) {
    width: 100%;
    height: 100%;
    border-radius: inherit;
  }

  &__error,
  &__placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    color: #c0c4cc;
    background-color: #f5f7fa;

    .error-text,
    .placeholder-text {
      font-size: 10px;
      margin-top: 2px;
      text-align: center;
      line-height: 1;
    }
  }

  &__error {
    color: #f56c6c;
  }
}
</style>
